﻿////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// <copyright>Copyright 2008-2011 Andreas Huber Doenni</copyright>
// Distributed under the Microsoft Public License (Ms-PL).
// See accompanying file License.txt or copy at http://mews.codeplex.com/license.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Mews.Net
{
    /// <summary>Is the base class of all commands that are non-pipelinable.</summary>
    /// <typeparam name="TClient">The type of the client the command is for.</typeparam>
    /// <typeparam name="TResponse">The type of the response the command will produce.</typeparam>
    /// <remarks>
    /// <para>Concrete commands never derive directly from this class, but subclass protocol-specific variants, like e.g.
    /// <see cref="Nntp.NonPipelinableCommand{T}"/>.</para>
    /// <para>See <a href="http://www.ietf.org/rfc/rfc3977.txt">RFC 3977</a> 3.5. for an description of pipelining. Note
    /// that the concept is universal and does not just apply to NNTP.</para>
    /// <para>A non-pipelinable command may have multiple stages, so <see cref="ReceiveResponse"/> allows for the
    /// creation of multiple <see cref="ResponseStream"/> and <see cref="CommandStream"/> objects.</para>
    /// </remarks>
    /// <threadsafety static="true" instance="false"/>
    public abstract class NonPipelinableCommandBase<TClient, TResponse> : CommandBase<TClient, TResponse>
        where TClient : IClient<TClient>
        where TResponse : IResponse
    {
        internal NonPipelinableCommandBase()
        {
        }

        internal sealed override bool IsPipelinable
        {
            get { return false; }
        }

        internal sealed override TResponse ReceiveResponseCore(
            CreateResponseStreamCallback createResponseStreamCallback,
            CreateCommandStreamCallback createCommandStreamCallback)
        {
            return this.ReceiveResponse(createResponseStreamCallback, createCommandStreamCallback);
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>When overridden in a derived class, receives the response to the first stage of the command and
        /// then possibly handles the second and subsequent stages.</summary>
        /// <param name="createResponseStreamCallback">The method that creates a stream, through which the response to
        /// the oldest stage command can be read, see <see cref="CreateResponseStreamCallback"/>.</param>
        /// <param name="createCommandStreamCallback">The method that creates a stream, through which a stage command
        /// can be written, see <see cref="CreateCommandStreamCallback"/>.</param>
        /// <returns>The response.</returns>
        /// <remarks>
        /// <para>Commands with only one stage simply create a <see cref="ResponseStream"/> object by calling
        /// <paramref name="createResponseStreamCallback"/> and then construct and return a response object with the
        /// data gathered through the <see cref="ResponseStream.InitialLineWords"/> and
        /// <see cref="ResponseStream.Read"/> members.</para>
        /// <para>The first stage of commands with 2 stages is handled as described above for one-stage commands, but of
        /// course without returning a response object. Stage two is then initiated by calling
        /// <paramref name="createCommandStreamCallback"/> and sending the bytes that make up the second stage. Finally,
        /// stage two (and with it the whole command) is completed by calling
        /// <paramref name="createResponseStreamCallback"/> and then construct and return a response object with the
        /// data gathered through the <see cref="ResponseStream.InitialLineWords"/> and
        /// <see cref="ResponseStream.Read"/> members.</para>
        /// <para>Overrides must not swallow any exceptions thrown from <paramref name="createResponseStreamCallback"/>,
        /// <paramref name="createCommandStreamCallback"/>, <see cref="ResponseStream"/> methods or
        /// <see cref="CommandStream"/> methods.</para>
        /// </remarks>
        protected abstract TResponse ReceiveResponse(
            CreateResponseStreamCallback createResponseStreamCallback,
            CreateCommandStreamCallback createCommandStreamCallback);
    }
}
